#coding=utf-8
# revision_panel.py
#
# Copyright 2011 Paul Gluchow <pgsurp@googlemail.com>
# Copyright 2012-2013 Maxim Petrov <maximpetrov@yahoo.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 3.


from panel import Panel
import curses
import datetime
from controls import Pad, split_string
from revision_filter_window import RevisionFilterWindow
from revision_config_window import RevisionConfigWindow
from tag_window import TagWindow

class RevisionPanel(Panel):
    """
    Abstract class for draw panel with table of revisions.
    Childs of this class must implement way of getting revisions.
    """
    __subwindow = None
    __hgutil = None
    _main_window = None
    # width of visible subwindow
    __width = 0
    # height of visible subwindow
    __height = 0
    __x = 0
    __y = 0


    ###### for revision details pad #######

    # pad where the revision details are printed
    __pad_details = None



    ###### separating table and revision details pads #######
    # y offset, where details pad begins
    __pad_offset = 0



    ###### for revision table pad #######

    # pad where the table is printed
    __pad_revisions = None

    # revision panel width and height
    __revision_pad_w = 0
    __revision_pad_h = 0

    # current position of highlighted line
    # first two lines are table header and horizontal line
    __sel_pos = 0

    # first two lines are table header and horizontal line
    __HEADER_SIZE = 2

    # maximum width of all columns which should be printed
    __max_width_text = 0

    # currently printed and filtered log
    # a log has the following structure:
    # [ (rev_int, 'hash', 'branch', 'user', mercurial_date, 'message',
    #         {'extra':'value'}, ['file', 'file', ...]), (...) ]
    __log_array = None

    # same log array, but not filtered. used for resetting filter options
    __log_array_unmodified = []

    # maximum length of each revision detail (column)
    # value of -1 means: the whole string without being cut
    __max_len_filter = None

    # index of revision_detail which should be printed
    __elements_to_print = []

    # filter using flag
    __filter_setted = False
    __need_redraw_table = False

    # panel active flag
    __is_active = False

    # revision selected for compare
    _diff_sel_rev = -1

    # message shown in revision panel if filter options don't have any result revision
    __no_revision_after_filter_message = _(u'No revisions found for these filter options!\n'
        u'Reset filter options to show revisions of selected files!')

    # Revision detail indexes
    REVISION    = 0
    HASH        = 1
    BRANCH      = 2
    BOOKMARKS   = 3
    USER        = 4
    DATE        = 5
    MESSAGE     = 6
    EXTRA       = 7
    FILES       = 8

    # names of revision_log elements - used for table header
    __log_elements = []

    __log_array_ = []
    __column_widths_ = None

    @property
    def __log_array(self):
        """Getter for __log_array"""
        
        return self.__log_array_

    @__log_array.setter
    def __log_array(self, log_array):
        """Setter for __log_array"""

        self.__log_array_ = log_array
        if self.__max_len_filter is not None:
            self.__column_widths = self.__get_min_len_array(
                self.__format_log_elements(self.__log_array, 
                [-1] * len(self.__max_len_filter), True))
        else:
            self.__column_widths = None
    
    @property
    def __column_widths(self):
        """Getter for __column_widths"""
        
        if self.__column_widths_ is not None:
            result = []
            for i, cw in enumerate(self.__column_widths_):
                if self.__max_len_filter[i] != -1:
                    result.append(min(self.__max_len_filter[i], cw))
                else:
                    result.append(cw)
            return result
        else:
            return None
    
    @__column_widths.setter
    def __column_widths(self, column_widths):
        """Setter for __column_widths"""
        
        self.__column_widths_ = column_widths

    def __init__(self, main_window):
        # divides the pad (percentage)
        self.__pad_divisor = float(main_window.settings.get(type(self).__name__, u'pad_divisor', False) or 0.5)
        self.hotkeys = {4: _(u'Filter'), 6: _(u'Tag'), 8: _(u'Configure')}
        self._main_window = main_window
        self.__pad_revisions = Pad(self._main_window,
                         lambda w, h: (1, 1, w - 2, self.__pad_offset),
                         self.__get_rev_table_contents)
        self.__pad_details = Pad(self._main_window,
                         lambda w, h: (1, 1 + self.__pad_offset, w - 2, h - 2 - self.__pad_offset),
                         self.__get_rev_details_table_contents)
        self.__pad_details.x_shift = 0
        self.__pad_details.y_shift = 0
        self.__pad_revisions.x_shift = 0
        self.__pad_revisions.y_shift = 0

        self.__log_elements = [_(u'rev'), _(u'hash'), _(u'branch'), _(u'bookmarks'), _(u'user'), _(u'date'), _(u'message'), _(u'extra'), _(u'files')]
        self.__log_array = []
        self.show_all_revs = False
        max_len_filter_dict = main_window.settings.get(type(self).__name__, u'max_len_filter')
        max_len_filter = [-1, -1, -1, -1, 15, -1, 40, -1, -1]
        if max_len_filter_dict is not None:
            max_len_filter_dict = dict((f[u'name'], f[u'value']) for f in max_len_filter_dict)
            for i, elem in enumerate(self.__log_elements):
                if max_len_filter_dict.has_key(elem):
                    max_len_filter[i] = int(max_len_filter_dict[elem])
        self.set_max_len_filter(max_len_filter)
        self.__elements_to_print = main_window.settings.get(type(self).__name__, u'elements_to_print')
        if self.__elements_to_print is not None:
            self.__elements_to_print = [int(elem) for elem in self.__elements_to_print]
        else:
            self.__elements_to_print = [self.REVISION, self.DATE, self.USER, self.MESSAGE]
        self.__need_redraw_table = True
        self.__printed_details_lines = 0
        self.__ch = -1

    def redraw(self, sw, content_only=False):
        self.__pad_revisions.window = sw
        self.__pad_details.window = sw
        self.__y, self.__x = sw.getbegyx()
        self.__height, self.__width = sw.getmaxyx()
        self.__subwindow = sw
        if not self.__need_redraw_table:
            self.__draw_content()
        self.__upd(content_only)

    def handle_keypress(self, ch, uni):
        old_sel_pos = self.__sel_pos

        if ch == curses.KEY_END:
            self.__sel_pos = len(self.__log_array) - 1
            self.__pad_revisions.y_shift = self.__sel_pos - (self.__pad_offset - self.__HEADER_SIZE) + 1
            if self.__pad_revisions.y_shift < 0:
                self.__pad_revisions.y_shift = 0
            self.__pad_details.y_shift = 0

        elif ch == curses.KEY_HOME:
            self.__pad_revisions.y_shift = 0
            self.__pad_details.y_shift = 0
            self.__sel_pos = 0

        elif ch == curses.KEY_UP:
            if self.__log_array == []:
                return
            if self.__sel_pos > 0:
                self.__sel_pos -= 1
            if self.__pad_revisions.y_shift > self.__sel_pos:
                self.__pad_revisions.y_shift -= 1
            self.__pad_details.y_shift = 0

        elif ch == curses.KEY_DOWN:
            if self.__log_array == []:
                return
            if self.__sel_pos < len(self.__log_array) - 1:
                self.__sel_pos += 1
            if self.__sel_pos - self.__pad_revisions.y_shift > (self.__pad_offset - self.__HEADER_SIZE) - 1:
                self.__pad_revisions.y_shift += 1
            self.__pad_details.y_shift = 0
            
        elif ch == curses.KEY_PPAGE:
            if self.__log_array == []:
                return
            self.__sel_pos -= (self.__pad_offset - self.__HEADER_SIZE)
            self.__pad_revisions.y_shift -= (self.__pad_offset - self.__HEADER_SIZE)
            if self.__sel_pos < 0:
               self.__sel_pos = 0
            if self.__pad_revisions.y_shift < 0:
                self.__pad_revisions.y_shift = 0
            self.__pad_details.y_shift = 0

        elif ch == curses.KEY_NPAGE:
            if self.__log_array == []:
                return
            self.__sel_pos += (self.__pad_offset - self.__HEADER_SIZE)
            self.__pad_revisions.y_shift += (self.__pad_offset - self.__HEADER_SIZE)
            if self.__sel_pos > len(self.__log_array) - 1:
                self.__sel_pos = len(self.__log_array) - 1
            if self.__pad_revisions.y_shift > len(self.__log_array) - (self.__pad_offset - self.__HEADER_SIZE):
                self.__pad_revisions.y_shift = len(self.__log_array) - (self.__pad_offset - self.__HEADER_SIZE)
            self.__pad_details.y_shift = 0

        elif ch == curses.KEY_LEFT:
            self.__pad_revisions.x_shift -= 5
            if self.__pad_revisions.x_shift < 0:
                self.__pad_revisions.x_shift = 0

        elif ch == curses.KEY_RIGHT:
            self.__pad_revisions.x_shift += 5
            if self.__pad_revisions.x_shift > self.__max_width_text - self.__width + 2:
                self.__pad_revisions.x_shift = self.__max_width_text - self.__width + 2

        # Key F3 will show modal window with more details to selected revision
        #elif ch == curses.KEY_F3:
        #    if self.__log_array.__len__() > 0:
        #        # format before displaying the details (here without max length)
        #        log_arr = self.__format_log_elements(self.__log_array, [-1,-1,-1,-1,-1,-1,-1,-1])
        #        self._main_window.show_modal_window(RevDetailsWindow(
        #                self.__log_elements,
        #                log_arr[self.__sel_pos - self.__SEL_POS_MIN]))

        # Key F4 will show filter options modal window
        elif ch == curses.KEY_F4:
            #if self.__log_array.__len__() > 0:
            # set back select position to start at first position after filtering
            old_sel_pos = 0
            self.__filter_tf()

        # Key F6 to reset filter options.
        # Revisions of selected files (in tree) will be shown again.
        #elif ch == curses.KEY_F6:
            #self.__filter_setted = False
            #self.__log_array = self.__log_array_unmodified
            #self.__draw_content(self.__log_array)
            #self.__upd()

        # Key F6 to reset filter options.
        # Revisions of selected files (in tree) will be shown again.
        elif ch == curses.KEY_F6:
            self.__tag()

        # Key F8 will show filter options modal window
        elif ch == curses.KEY_F8:
            old_sel_pos = 0
            old_x_pos = 0
            self.__set_config()
            self.__draw_content()

        # Keys Control + Up
        elif ch == int(self._main_window.settings.get(u'Keycodes', u'KEY_CONTROL_UP', False) or -1):
            if self.__pad_details.y_shift > 0:
                self.__pad_details.y_shift -= 1


        # Keys Control + Down
        elif ch == int(self._main_window.settings.get(u'Keycodes', u'KEY_CONTROL_DOWN', False) or -1):
            # height of pad calculating depending on the percentage separation of the pads
            pad_h = self.__height - 2 - self.__pad_offset
            if self.__printed_details_lines - self.__pad_details.y_shift > pad_h:
                self.__pad_details.y_shift += 1
                
        elif ch == ord(' '):
            if self._diff_sel_rev == self.__sel_pos:
                self._diff_sel_rev = -1
            else:
                self._diff_sel_rev = self.__sel_pos

        else:
            return

        # highlight current selected revision and update the revision panel
        self.__select(old_sel_pos)
        self.__upd()

    def set_active(self):
        if not self.visible:
            return
        self.__is_active = True
        self.__upd()

    def set_inactive(self):
        if not self.visible:
            return
        self.__is_active = False
        self.__upd()

    def __select(self, old_sel_pos):
        """
        Функция снятия выделения с одной строки и установка на другую строку

        Аргументы:
        old_sel_pos - старое положение по y
        """
        pass

    def set_max_len_filter(self, max_len_filter_array):
        """
        function to set maximum length of columns in the revision-log table
        array structure: ['rev', 'hash', 'branch', 'bookmarks', 'user', 'date', 'message', 'extra', 'files']
        set -1 for maximum length
        """
        self.__max_len_filter = max_len_filter_array
        self.__upd()


    def __get_min_len_array(self, array):
        """
        calculate minimal amount of symbols for each column, to have the same width
        """

        result = [len(elem) for elem in self.__log_elements]

        for a in array:
            for j, elem in enumerate(a):
                length = len(elem)
                if length > result[j]:
                    result[j] = length
        return result


    def __format_log_elements(self, elements, max_len_filter_array, first_message_line):
        """
        Function format elements returned from get_log().  Now format only date.
        @param elements: array[revision_set][revision_detail]
        @param max_len_filter_array: array[revision_detail_max_length]
        @param first_message_line: if True take part of message before first '\n'

        @return: formatted elements_array[revision_set][revision_detail]
        """

        # changing the structure of log_elements from tuple to list
        # because tuples are immutable
        # structure of log_elements: [(tuple),(tuple), ... , (tuple)]
        # converting to: [[list], [list], ... , [list]]

        conv_elements = [list(elem) for elem in elements]

        for elem in conv_elements:
            for j, column in enumerate(elem):

                if j == self.DATE:
                    column = unicode(datetime.datetime.fromtimestamp(column[0]).
                                                            strftime('%x %X'))

                # convert list of file_names to a string, separated by whitespace
                if j == self.FILES:
                    column = u' '.join(column)

                # convert list of bookmarks to a string, separated by commas
                if j == self.BOOKMARKS:
                    column = u', '.join(column)

                # convert all element to unicode string
                column = unicode(column)

                # take first line from message-string if need
                if j == self.MESSAGE and first_message_line:
                    column = column.split('\n')[0]

                # set max length of string
                if max_len_filter_array[j] > -1:
                    column = column[:max_len_filter_array[j]]

                elem[j] = column
        return conv_elements

    def _get_table_content(self):
        """
        function to get content to show in table
        """
        pass

    def _update_content(self):
        """
        function to update the whole content
        """

        self.__draw_content(reload_content=True)
        self.__upd()

    def _no_revisions_string(self):
        """
        function for get string reports that there are no revisions to show
        """

        pass

    def __draw_content(self, log_array=None, reload_content=False):
        """
        Function to draw the content
        First table (first pad) is printed.
        Then revision details (second pad) are printed to the currently
        selected revision in table.

        @param:
            log_array - List of revisions to be printed in the table.
                If no log array is given, use self.__log_array.
            reload_content - if True function _get_table_content()
                    from the children will be called
        """
        if reload_content:
            self.__pad_revisions.x_shift = 0
            self.__pad_revisions.y_shift = 0
            self.__pad_details.x_shift = 0
            self.__pad_details.y_shift = 0
            self.__revision_pad_w = 0
            self.__revision_pad_h = 0

        offset_column = 2 # space between columns

        # if need to reload, get new table content
        if reload_content:
            content = self._get_table_content()
            # None in contents means that it isn't changed (commonly
            # because of error). For empty content function return []
            if content is not None:
                self.__log_array_unmodified = content
                self.__filter_setted = False
            self.__log_array = self.__log_array_unmodified
            log_array = self.__log_array
        # else use given log
        elif log_array is not None:
            self.__log_array = log_array
        # or saved log
        else:
            log_array = self.__log_array
        # if no revisions are found, print an information about this
        if len(log_array) == 0:
            self.__revision_pad_h = self.__height - 2
            self.__max_width_text = self.__width - 2
            self.__revision_pad_w = self.__max_width_text
            # clear details pad
            return

        if self.__sel_pos < 0 or self.__sel_pos > len(self.__log_array) - 1:
            self.__sel_pos = 0

        # calculate printed text width
        text_width = 0
        for i in self.__elements_to_print:
            text_width += self.__column_widths[i] + offset_column
        self.__max_width_text = max(text_width - offset_column, self.__width - 2)

        self.__revision_pad_h = len(log_array) + 5
        self.__revision_pad_w = self.__max_width_text



    def __get_rev_table_contents(self, s, c):
        """
        Function returns the revision table content for pad.

        Keyword arguments:
            s - first displayable line of pad content
            c - pad displayable content length

        Return value:
            Return list of tuples (string, None, None)
        """

        if len(self.__log_array) == 0:
            if self.__filter_setted:
                for l in self.__no_revision_after_filter_message.splitlines():
                    yield (l[self.__pad_revisions.x_shift:], None, None)

            else:
                yield (self._no_revisions_string()[self.__pad_revisions.x_shift:],
                                               None, None)
            return

        offset_column = 2 # space between columns

        log_array = self.__format_log_elements(self.__log_array[s:s+c-self.__HEADER_SIZE],
                                                self.__max_len_filter, True)

        # print table header
        yield (u''.join(unicode(self.__log_elements[i]) + \
            (self.__column_widths[i] + offset_column - len(unicode(self.__log_elements[i]))) * u' '
            for i in self.__elements_to_print), None, None)

        # horizontal line separating table header and content

        yield (u'-' * self.__revision_pad_w, None, None)

        # print the content of the table
        for i, elem in enumerate(log_array):
            attr = None
            is_sel = s + i == self.__sel_pos and self.__is_active
            is_diff_sel = s + i == self._diff_sel_rev

            if is_sel:
                if is_diff_sel:
                    attr = curses.A_REVERSE | curses.A_BOLD
                else:
                    attr = curses.A_REVERSE
            elif is_diff_sel:
                attr = self._main_window.color_scheme.selected | curses.A_BOLD
            
            if attr is not None:
                attr = [(0, self.__revision_pad_w, attr)]

            yield (u''.join((elem[pr_elem] + \
                (self.__column_widths[pr_elem] + offset_column - len(elem[pr_elem])) * u' '
                for pr_elem in self.__elements_to_print)), None, attr)



    def __get_rev_details_table_contents(self, s, c):
        """
        Function returns the revision details table content for pad.

        Keyword arguments:
            s - first displayable line of pad content
            c - pad displayable content length

        Return value:
            Return list of tuples (string, None, None)
        """

        self.__printed_details_lines = 0

        if len(self.__log_array) == 0:
            return

        log_details = self.__format_log_elements([self.__log_array[self.__sel_pos]], [-1,-1,-1,-1,-1,-1,-1,-1,-1], False)[0]

        # width is fixed and with -2 everything fits in the visible
        # field of details_pad
        pad_w_visible = self.__width - 2

        # amount of printed lines to scroll, if there are more lines
        # than it can be fitted to the pad height

        yield (u'-' * pad_w_visible, None, None)
        self.__printed_details_lines += 1

        yield (_(u'Revision details for revision {0}').format(unicode(log_details[self.REVISION])), None, None)
        self.__printed_details_lines += 1

        yield (u'-' * pad_w_visible, None, None)
        self.__printed_details_lines += 1

        # calculate starting point of revision detail content
        max_width_detailname = max(len(elem) for elem in self.__log_elements)

        # space between columns
        space = 3
        content_start = max_width_detailname + space
        # calculate max width of detail_content_column
        max_width_content = pad_w_visible - (max_width_detailname + 3)

        for i in range(len(log_details)):

            # name of revision detail (header)
            str_tmp = unicode(self.__log_elements[i]) + u":"

            # each filename in one line
            if i == self.FILES:
                new_string_list = u' '.join(split_string(log_details[i], max_width_content)).split(u' ')
            else:
                new_string_list = split_string(log_details[i], max_width_content)
            for string_line in new_string_list:
                str_tmp = str_tmp + (content_start - len(str_tmp)) * u' ' + string_line.replace(u'\n', u'')
                self.__printed_details_lines += 1
                if (self.__printed_details_lines - 3 - 1) in range(s, s+c-3):
                    yield (str_tmp, None, None)
                str_tmp = u''



    def __upd(self, content_only=False):
        """
        function for updating the panel
        """
        if not self.visible:
            return
        if self.__need_redraw_table:
            self.__draw_content(reload_content=True)
        self.__need_redraw_table = False
        if content_only:
            return
        self.__subwindow.erase()
        self.__subwindow.box()

        if self.__height > 1:
            title = self._panel_header[:self.__width - 1]
            if self.__filter_setted:
                title += u' [' + _(u'Filtered') + u']'
            self.__subwindow.addstr(0, 1, self._main_window.str_to_external(title))

        self.__pad_offset = int(float(self.__height - 2) * self.__pad_divisor)

        # refresh the revision table pad
        if self.__height > 4 and self.__width > 2:
            self.__pad_revisions.draw(self.__x, self.__y, self.__width, self.__height)
            pass


        # refresh the revision details pad
        if self.__height > 4 and self.__width > 2:
            self.__pad_details.draw(self.__x, self.__y, self.__width, self.__height)
            pass
        self.__subwindow.refresh()

    def __filter_tf(self, all_elem = False):
        """
        Function to call the RevisionFilterWindow (Modal Window) and to handle
        the result. Result is a dictionary with:
            key - name of revision detail
            value - filter option set by user (string)

        @param:
            all_elem - Flag - True, if filter should be used for all files in repository

        Function is looping through all revisions in revision_panel
        and combining the search results with AND operator.
        If result is true, the current revision in loop will be added
        to a new list. This list is used to be filtered again, if needed.

        """

        # inf: result dictionary with values the user set in filter text fields
        # structure:  {'button': 'OK', 'user': 'user@xyz.com', 'revision': '2', ... }
        inf = self._main_window.show_modal_window(RevisionFilterWindow())
        if inf[u'button'] == u'Cancel':
            return
        self.__filter_setted = False
        all_elem = inf[u'all']
        
        # use current log array (revisions to selected files)
        if not all_elem:
            log_array = self.__log_array
        # use filter for all files in repository
        else:
            log_array = self._main_window.repo_util.get_full_log()
            log_array.reverse()


        elements_to_filter = []
        for key in inf.keys():
            if self.__log_elements.count(key) > 0:
                val = inf[key]
                if len(val) > 0:
                    elements_to_filter.append((self.__log_elements.index(key), val))
        if len(elements_to_filter) == 0:
            if not all_elem:
                self.__draw_content(self.__log_array_unmodified)
            else:
                self.__draw_content(log_array)
            return
        self.__filter_setted = True
        log_arr_filtered = []
        for rev in log_array:
            result = True
            for el in elements_to_filter:
                if el[0] == self.REVISION:
                    result = result and Filter.is_in_revision(rev[el[0]], el[1])
                elif el[0] == self.HASH:
                    result = result and Filter.is_in_hash(rev[el[0]], el[1])
                elif el[0] == self.BRANCH:
                    result = result and Filter.is_in_branch(rev[el[0]], el[1])
                elif el[0] == self.BOOKMARKS:
                    result = result and Filter.is_in_bookmarks(rev[el[0]], el[1])
                elif el[0] == self.USER:
                    result = result and Filter.is_in_user(rev[el[0]], el[1])
                elif el[0] == self.DATE:
                    result = result and Filter.is_in_date(rev[el[0]], el[1])
                elif el[0] == self.MESSAGE:
                    result = result and Filter.is_in_message(rev[el[0]], el[1])
                elif el[0] == self.EXTRA:
                    result = result and Filter.is_in_extra(rev[el[0]], el[1])
                elif el[0] == self.FILES:
                    result = result and Filter.is_in_files(rev[el[0]], el[1])
            if result:
                log_arr_filtered.append(rev)

        self.__draw_content(log_arr_filtered)
        self.__upd()



    def __tag(self):
        """
        Функция __tag вызывает модальное окно для тегирования ревизии
        """
        code = self.get_selected_revision_number()
        if code is None:
            revision = u''
        else:
            revision = unicode(code)
        inf = self._main_window.show_modal_window(TagWindow(revision))
        if inf.get(u'button') == u'Cancel':
            return
        self._main_window.repo_util.tag(revision, inf.get(u'tag'), inf.get(u'local'))
        self.__upd()



    def __set_config(self):
        """
        Function __set_config creates a list of configure options which
        can be set to the revision panel and creates a RevisionConfigWindow
        using this option list.
        After RevisionConfigWindow exits, this function get a result dictionary
        with configure options and set this modifications to revision panel.

        """
        # option name used in config modal window
        divide_option = _(u'divide screen')
        order_option = _(u'columns order')

        # option names of revision table columns used in config modal window
        # copy of original list (also aborting configurations process would
        # otherwise change original list)
        el_to_print = list(self.__elements_to_print)
        # remove first element -> revision
        el_to_print.pop(0)

        # order_str is used in config_modal_window as preallocated option value
        order_str = unicode(el_to_print)
        # remove the brackets and spaces used in lists
        order_str = order_str.replace(u'[', u'')
        order_str = order_str.replace(u']', u'')
        order_str = order_str.replace(u' ', u'')


        # create configure modal window with conf_list
        conf_list = []
        # structure of each element in list:
        # [option_name, preallocated_option_value, option_information_for_user, max_length]
        for i in range(1, len(self.__max_len_filter)):
            conf_list.append([unicode(i) +u': ' + self.__log_elements[i],
                    unicode(self.__max_len_filter[i]),
                    _(u'(x symbols to print, max: -1)'), 4])
        conf_list.append([order_option, order_str, _(u'(indexes, comma separated)'), 20])
        conf_list.append([divide_option, unicode(int(self.__pad_divisor * 100)), u'% (20 - 80 %)', 2])

        # inf: result dictionary with values the user set in filter text fields
        inf = self._main_window.show_modal_window(RevisionConfigWindow(conf_list))
        if inf[u'button'] == u'Cancel':
            return


        ### set all configurations done in modal window

        # get the table column size set in modal window
        # firstly max size for revision is set
        max_size = [-1]
        for i in range(1, len(self.__max_len_filter)):
            conf_option = unicode(i) +u': ' + self.__log_elements[i]
            try:
                size = int(inf[conf_option])
            except:
                pass
            else:
                if size == 0 or size < -1:
                    max_size.append(self.__max_len_filter[i])
                else:
                    max_size.append(size)
        self.set_max_len_filter(max_size)
        max_len_filter_dict = [{u'name': self.__log_elements[i], u'value': unicode(ms)} for i, ms in enumerate(max_size)]
        self._main_window.settings.set(type(self).__name__, u'max_len_filter', max_len_filter_dict)


        # get the pad_divisor set in modal window
        try:
            divisor = float(inf[divide_option]) / 100
        except:
            pass
        # if divisor is between 20 and 80 %, set the new value
        if divisor <= 0.8 and divisor >= 0.2:
            self.__pad_divisor = divisor
            self._main_window.settings.set(type(self).__name__, u'pad_divisor', divisor)
        else:
            pass

        # get the elements and the order of elements to print in table
        order_list_str = inf[order_option].split(u',')
        # first element is revision
        order_list_int = [0]
        for order in order_list_str:
            try:
                column_index = int(order)
                if column_index >= 0 and column_index < len(self.__log_elements):
                    order_list_int.append(int(order))
            except:
                pass

        self.__elements_to_print = order_list_int
        self._main_window.settings.set(type(self).__name__, u'elements_to_print', order_list_int)


    def get_selected_revision_number(self):
        """
        Returns number of the selected revision. If there are no revisions
        to select, returns None
        """
        if not self.__log_array:
            return None
        else:
            return  self.__log_array[self.__sel_pos][self.REVISION]

    def get_diff_selected_revision_number(self):
        """
        Returns number of the revision marked for diff. If there are no revisions
        to select, returns None
        """
        if not self.__log_array or self._diff_sel_rev == -1:
            return None
        else:
            return self.__log_array[self._diff_sel_rev][self.REVISION]



class Filter(object):
    """
    class Filter
    Separating revision panel from filtering.
    Using static methods to call comparing functions.
    """

    @staticmethod
    def is_in_revision(revision, filter_string):
        """
        Function is_in_revision to compare two parameter.
        Revision specific comparing.

        @param:
            revision - revision (integer)
            filter_string - string the user typed in a textfield

        @return:
            result of comparing (True/False)
        """
        try:
            rev = int(filter_string)
        except Exception:
            return False

        return rev == revision

    @staticmethod
    def is_in_hash(hash_str, filter_string):
        """
        Function is_in_hash to compare two parameter.
        Hash specific comparing.

        @param:
            hash_str - hash (string)
            filter_string - string the user typed in a textfield

        @return:
            result of comparing (True/False)
        """
        return hash_str.find(filter_string) != -1

    @staticmethod
    def is_in_branch(branch_str, filter_string):
        """
        Function is_in_revision to compare two parameter.
        Revision specific comparing.

        @param:
            revision - revision (integer)
            filter_string - string the user typed in a textfield

        @return:
            result of comparing (True/False)
        """
        branch_str = branch_str.lower()
        filter_string = filter_string.lower()
        return branch_str.find(filter_string) != -1

    @staticmethod
    def is_in_bookmarks(bookmarks, filter_string):
        """
        Function is_in_bookmarks to compare two parameter.
        Bookmarks specific comparing.

        @param:
            bookmarks - bookmarks (string list)
            filter_string - string the user typed in a textfield

        @return:
            result of comparing (True/False)
        """
        bookmarks_set = set((b.lower() for b in bookmarks))
        filter_bookmarks_set = set((fb.strip() for fb in filter_string.lower().split(",")))
        return len(bookmarks_set.intersection(filter_bookmarks_set)) != 0

    @staticmethod
    def is_in_user(user_str, filter_string):
        """
        Function is_in_user to compare two parameter.
        User specific comparing.

        @param:
            user_str - user name (string)
            filter_string - string the user typed in a textfield

        @return:
            result of comparing (True/False)
        """
        user_str = user_str.lower()
        filter_string = filter_string.lower()
        return user_str.find(filter_string) != -1

    @staticmethod
    def is_in_date(date, filter_string):
        """
        Function is_in_date to compare two parameter.
        Date specific comparing.

        @param:
            date - date from mercurial API (a tuple (float, integer))
            filter_string - string the user typed in a textfield

        @return:
            result of comparing (True/False)
        """
        date_string = unicode(datetime.datetime.fromtimestamp(date[0]).strftime('%x %X'))
        return date_string.find(filter_string) != -1

    @staticmethod
    def is_in_message(message_str, filter_string):
        """
        Function is_in_message to compare two parameter.
        Message specific comparing.

        @param:
            message_str - message (string)
            filter_string - string the user typed in a textfield

        @return:
            result of comparing (True/False)
        """
        return message_str.find(filter_string) != -1

    @staticmethod
    def is_in_extra(extra, filter_string):
        """
        Function is_in_extra to compare two parameter.
        Extra specific comparing.

        @param:
            extra - message (string)
            filter_string - string the user typed in a textfield

        @return:
            result of comparing (True/False)
        """
        return extra.find(filter_string) != -1

    @staticmethod
    def is_in_files(files, filter_string):
        """
        Function is_in_files to compare two parameter.
        Files specific comparing.

        @param:
            files - files (string)
            filter_string - string the user typed in a textfield

        @return:
            result of comparing (True/False)
        """
        return files.find(filter_string) != -1

